vue0.11.9源码 src_util_merge-option.js
Last Updated:2023-08-02
src/util/merge-option.js 源码
整个逻辑入口行 L236
核心逻辑就是function mergeOptions
根据其注释(L227),该函数的2个重点:
- 返回一个新Object
- 第三个入参vm,暗示了是'实例化'的时候,还是'继承'的时候调用了此函数
函数mergeOptions逻辑第一部分——guardComponents
根据L206的意思,这个函数是用来将"组件"字段(即: components)的每一项,都改造成具体的Constructor
(即构造函数)。
再通俗一点的就是,有可能用户传了一个字符串"Custom"组件,那么这个函数会将这个字符串"Custom"转换成具体的Custom组件
的构造函数(并且这个构造函数对应的类的父类是调用mergeOptions时候的this)。
// 假设有
components['Custom'] = 'Custom'
// 将其变成
components['Custom'] = Class Custom {} // 或者 components['Custom'] = function Custom(){}
// 当然,vue这里,主要是将下面这个
components['Custom'] = { name: 'Custom', a1: '其他乱七八糟的属性,关键是这是一个Object的options ', data: 'data属性', methods: 'methods属性' }
// 将其通过guardComponents, 变成一个构造函数(构造函数包含其它的属性,譬如a1)
components['Custom'] = Class Custom { ... } // 根据策略规则,将a1属性相应转换成Custom的'私有属性'、'公共属性'、'甚至抛弃';以此类推,处理data、methods等等
_.Vue.extend L220
就是“类的继承”
这个_.Vue
是在src/vue.js
里头被挂载到_
上的。L84
从而这个_.Vue.extend
,就是src/api/global.js
里头的extend
这个_.Vue.extend
的源码是: L34
这个"类的继承",父类是: this L42 , 应该是调用mergeOptions
时候的this。init.js/L74
子类都是this的子类,也就是"子组件"
函数mergeOptions逻辑第二部分——各种merge
data属性的合并策略
!vm === true (使用Vue.extend触发的merge)
// in a Vue.extend merge, both should be functions
if (!childVal) {
return parentVal
}
if (typeof childVal !== 'function') {
_.warn(
'The "data" option should be a function ' +
'that returns a per-instance value in component ' +
'definitions.'
)
return parentVal
}
if (!parentVal) {
return childVal
}
// when parentVal & childVal are both present,
// we need to return a function that returns the
// merged result of both functions... no need to
// check if parentVal is a function here because
// it has to be a function to pass previous merges.
return function mergedDataFn () {
return mergeData(
childVal.call(this),
parentVal.call(this)
)
}
这种情况,按照注释的意思是,基本上都是调用Vue.extend()的case,那么入参parentVal
和childVal
都应该是function
并且,最后返回的是一个function
// 返回的是function,这和有vm时候的返回值不一样
return function mergedDataFn () {
return mergeData(
childVal.call(this),
parentVal.call(this)
)
}
问(TBC): 上述代码里头,childVal.call(this)和parentVal.call(this)里头的this指向什么?
!!vm === true (合并实例,返回一个 raw object,非函数)
// instance merge, return raw object
var instanceData = typeof childVal === 'function'
? childVal.call(vm)
: childVal
var defaultData = typeof parentVal === 'function'
? parentVal.call(vm)
: undefined
if (instanceData) {
return mergeData(instanceData, defaultData)
} else {
return defaultData
}
关键的合并策略,就在于mergeData
函数
function mergeData (to, from) {
var key, toVal, fromVal
for (key in from) {
toVal = to[key]
fromVal = from[key]
if (!to.hasOwnProperty(key)) {
to.$add(key, fromVal)
} else if (_.isObject(toVal) && _.isObject(fromVal)) {
mergeData(toVal, fromVal)
}
}
return to
}
将from里头有,而to里头没有的属性,都"复制"到to上
其中这个: 复制 的操作,其实是to.$add
这个to.$add
源码object.js#L4
就是添加"响应式"的数据
el合并规则
#84 值都是“函数”, 合并规则是: 覆盖调用函数
Hooks and param attributes 合并规则
#104 created,ready,attached等等生命周期 合并规则是: 数组压进去
assets合并规则
#126 directives, filters, partials, transitions, components等
函数返回一个新的对象
先构建一个"option链"(类似原型链)(继承)
var ret = Object.create(
vm && vm.$parent
? vm.$parent.$options[key]
: _.Vue.options[key]
)
然后是合并策略是: 后者覆盖前者
watch,events合并策略
先构建一个"原型链"(继承)
每一个Key对应的值都转换成数组,往数组里一个个推"值"
methods,computed合并策略
先构建一个"原型链"(继承)
合并策略:非数组,是后者覆盖前者
<## Object.create(null) #L16
var strats = Object.create(null)
为何要Object.create(null)
?
要想了解这一点,需要先了解原型链
、原型
、__proto__
、prototype
、Object.prototype
参考文献: Object.prototype.__proto__、这属性逐渐淘汰不建议使用 从__proto__和prototype来深入理解JS对象和原型链 Object.create 你不知道的javascript之Object.create 和new区别
所以上述问题的结论就是:
在实践中,以
null
为原型的对象通常用于作为 map 的替代。因为Object.prototype
原型自有的属性的存在会导致一些错误
譬如
var obj = {}
var obj2 = Object.create(null)
console.log(obj.toString === undefined) // false toString是Object.prototype里头有的
console.log(obj2.toString === undefined) // true